package sigma;
import java.util.ArrayList;
import java.io.*;

public class Activity {
    
    //VARIABLE DECLARATION
    ArrayList<Edge> _edges = new ArrayList<Edge>();
    Node _node;
    Edge _edge;
    
    String _startNodeName;
    String _stateChanges;
    String _parameters;
    String _endNodeName;
    String _condition;
    String _delay;
    String _attributes;
    
    public Activity(int activityType, int arg) {
        switch (activityType) {
            case 1 : makeStartNode(arg); 
                     break;
            case 2 : makeEndNode(arg);   
                     break;
            case 3 : makeSortNode(arg);            
                     break;
            case 4 : makeArriveNode(arg);
                     break;
            case 5 : makeLeaveNode();
                     break;
            case 6 : makeRunNode();
                     break;
            case 7 : makeInitNode();
                     break;
            default: System.out.println("Something broke...");
        }
    }
    
    public String getStartNodeName() {
        return _startNodeName;
    }
    
    public void makeStartNode(int procedureID) {
        /*
         * Make the node
         */
        _startNodeName = "P" + procedureID + "_START";
        _stateChanges = "STAFF=STAFF-1, QPRD[" + procedureID + "]=QPRD[" + procedureID + "]-GET{FST;" + procedureID + "}";
        _parameters = "";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * _START -> _LEAVE
         */
        
        _endNodeName = "P" + procedureID + "_LEAVE";
        _condition = "1==1";
        _delay = "DISK{PROCEDURE.DAT;(PRDDATA+1)*" + procedureID + "+2}";
        _attributes = "ENT[0],ENT[1],ENT[2],ENT[3],ENT[4]+1";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void makeEndNode(int procedureID) {
        /*
         * Make the node
         */
        _startNodeName = "P" + procedureID + "_LEAVE";
        _stateChanges = "ENT[5]=0,STAFF=STAFF+PUT{INC;ENT[5]}";  //This is a custom code
        _parameters = "ENT[0],ENT[1],ENT[2],ENT[3],ENT[4]";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * _LEAVE -> SORT
         */
        _endNodeName = "SORT";
        _condition = "1==1";
        _delay = "0";
        _attributes = "ENT[5]";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
        
        /*
         * _LEAVE -> _START
         */
        _startNodeName = "P" + procedureID + "_START";
        _condition = "QPRD[" + procedureID + "]>0";
        _delay = "0";
        _attributes = "";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void makeSortNode(int totalNumOfProcedures) {
        /*
         * Make the node
         */
        _startNodeName = "SORT";
        _stateChanges = "QPRD[NEXTPRD]=QPRD[NEXTPRD]+1";
        _parameters = "NEXTPRD";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * SORT -> _START
         */
        for (int procedureCounter = 0; procedureCounter <= totalNumOfProcedures; procedureCounter++) {
            _endNodeName = "P" + procedureCounter + "_START";
            _condition = "NEXTPRD==" + procedureCounter + "&&STAFF>0";
            _delay = "0";
            _attributes = "";
            
            _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
            _edges.add(_edge);
        }
        
        /*
         * SORT -> LEAVE
         */
        _endNodeName = "LEAVE";
        _condition = "NEXTPRD==0";
        _delay = "0";
        _attributes = "";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void makeArriveNode(int totalNumOfPatientTypes) {
        /*
         * Make the node
         */
        _startNodeName = "ARRIVE";
        _stateChanges = "PID=PID+1,ENT[0]=PID,ENT[1]=CLK,ENT[2]=DISK{PATIENT.DAT;PDATA*(PTYPE-1)+3},ENT[3]=PTYPE,ENT[4]=0,ENT[5]=DISK{PATIENT.DAT;PDATA*(PTYPE-1)+4},QT=QT+PUT{INC;ENT[5]}";
        _parameters = "PTYPE";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * ARRIVE -> SORT
         */
        _endNodeName = "SORT";
        _condition = "1==1";
        _delay = "0";
        _attributes = "ENT[5]";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
        
        /*
         * ARRIVE -> ARRIVE
         */
        _endNodeName = "ARRIVE";
        _condition = "1==1";
        _delay = "DISK{PATIENT.DAT;PDATA*(PTYPE-1)+2}";
        _attributes = "PTYPE";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void makeLeaveNode() {
        /*
         * Make the node
         */
        _startNodeName = "RUN";
        _stateChanges = "QLEAVE=QLEAVE+GET{FST;0}";
        _parameters = "";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
    }

    public void makeRunNode() {
        /*
         * Make the node
         */
        _startNodeName = "RUN";
        String initilization = "";
        _stateChanges = "PMAX=DISK{INIT.DAT;1},PRDMAX=DISK{INIT.DAT;2},PDATA=DISK{INIT.DAT;3},PRDDATA=DISK{INIT.DAT;4},RNK[0]=2,RNK[1]=2,RNK[2]=2,RNK[3]=2,RNK[4]=2,RNK[5]=2,STAFF=10";
        _parameters = "";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * RUN -> INIT
         */
        _endNodeName = "INIT";
        _condition = "1==1";
        _delay = "0";
        _attributes = "1";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void makeInitNode() {
        /*
         * Make the node
         */
        _startNodeName = "INIT";
        _stateChanges = "";
        _parameters = "ITER";
        
        _node = new Node(_startNodeName, _stateChanges, _parameters);
        
        /*
         * INIT -> INIT
         */
        _endNodeName = "INIT";
        _condition = "ITER<=PMAX";
        _delay = "0";
        _attributes = "ITER+1";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
        
        /*
         * INIT -> ARRIVE
         */
        _endNodeName = "ARRIVE";
        _condition = "1==1";
        _delay = "DISK{PATIENT.DAT;PDATA*(ITER-1)+2}";
        _attributes = "ITER";
        
        _edge = new Edge(_startNodeName, _endNodeName, _condition, _delay, _attributes);
        _edges.add(_edge);
    }
    
    public void outputToText(String file) {
        try {
            FileWriter outFile = new FileWriter(file);
            PrintWriter out = new PrintWriter(outFile);
            
            //Header
            out.println("void " + _node.getNodeName() + "() {");
            
            //Declare the _edge_condition array
            out.println("int _edge_condition[" + _edges.size() + "];");
            out.println();
            
            //Attributes/Parameters
            out.println("/* Attribute Value(s) Passed to this Event */");
            String[] parameters = _node.getNodeParameters().split(",");
            for (String str:parameters) { //Get elements of that array
                if (!str.isEmpty()) { //Only list parameters if string is nonempty
                    out.println(str + ";"); //One parameter per line
                }
            }
            out.println();
            
            //State Changes
            out.println("/* State Changes */");
            //Get the list of state changes and remove by commas (,)
            String[] stateChanges = _node.getNodeStateChanges().split(",");
            for (String str:stateChanges) { //Get elements of that array
                if (!str.isEmpty()) {
                    out.println(str + ";"); //One state change per line
                }
            }
            out.println();
            
            //Evaluate edge conditions
            out.println("/* Evaluate edge conditions now so that they will*/");
            out.println("/* not be changed by preemptive event execution  */");
            for (int edgeCounter = 0; edgeCounter < _edges.size(); edgeCounter++) {
                out.println("_edge_condition[" + edgeCounter + "] = ( " + _edges.get(edgeCounter).getCondition() + " );");
            }
            out.println();
            
            //Edge condition space
            out.println("/* Schedule Future Events */");
            for (int edgeCounter = 0; edgeCounter < _edges.size(); edgeCounter++) {
                out.println("if (_edge_condition[" + edgeCounter + "]) {");
                
                String[] attributes = _edges.get(edgeCounter).getAttributes().split(",");
                int transferCounter = 3;
                for (String str:attributes) { //Get elements of that array
                    if (!str.isEmpty()) { //Only increment and list attributes if string is nonempty
                        out.println("transfer[" + transferCounter + "] = " + str + ";"); //One attribute per line
                        transferCounter++;
                    }
                }
                out.println();
                
                out.println("for ( t_index=" + transferCounter + "; t_index<maxatr; t_index++) transfer[t_index] = 0.0;");
                out.println("event_time = current_time + " + _edges.get(edgeCounter).getDelay() + "");
                out.println("event_type = " + _edges.get(edgeCounter).getEndNode() + "_event;");
                out.println("schedule_event();");
                out.println("}");
            }
            
            out.println("}");
            out.close();
            
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}